package com.uinnova.product.eam.service.impl;


import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.uinnova.product.eam.base.exception.ServerException;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.model.CiInfoExtend;
import com.uinnova.product.eam.model.asset.EamCiRltCopyDTO;
import com.uinnova.product.eam.model.asset.EamCiRltCopyResult;
import com.uinnova.product.eam.model.asset.EamCopyRltVO;
import com.uinnova.product.eam.model.dto.DiagramSheetParam;
import com.uinnova.product.eam.model.dto.ExtendSheetInfo;
import com.uinnova.product.eam.service.DiagramSheetSvc;
import com.uinnova.product.eam.service.IEamCiSvc;
import com.uinnova.product.eam.service.es.IamsESCIPrivateSvc;
import com.uinnova.product.vmdb.comm.model.ci.CCcCi;
import com.uinnova.project.base.diagram.comm.model.ESDiagram;
import com.uinnova.project.base.diagram.comm.model.ESDiagramLink;
import com.uinnova.project.base.diagram.comm.model.ESDiagramNode;
import com.uinnova.project.base.diagram.comm.model.ESDiagramSheetDTO;
import com.uinnova.project.db.eam.ESDiagramDao;
import com.uinnova.project.db.eam.ESDiagramLinkDao;
import com.uinnova.project.db.eam.ESDiagramNodeDao;
import com.uinnova.project.db.eam.ESDiagramSheetDao;
import com.uino.bean.cmdb.base.*;
import com.uino.bean.cmdb.query.ESRltSearchBean;
import com.uino.dao.cmdb.ESCIClassSvc;
import com.uino.dao.util.ESUtil;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class DiagramSheetSvcImpl implements DiagramSheetSvc {

    @Autowired
    IEamCiSvc eamCiSvc;
    @Autowired
    IamsESCIPrivateSvc iamsESCIPrivateSvc;
    @Autowired
    IamsCIRltPrivateSvc iamsCIRltPrivateSvc;
    @Autowired
    ESDiagramDao esDiagramDao;
    @Autowired
    ESDiagramNodeDao esDiagramNodeDao;
    @Autowired
    ESDiagramLinkDao esDiagramLinkDao;
    @Autowired
    ESDiagramSheetDao esDiagramSheetDao;
    @Autowired
    ESCIClassSvc esciClassSvc;

    private static final Long DOMAIN_ID = 1L;
    private static final String CI_POSTFIX = "_副本";

    @Override
    public ExtendSheetInfo copySheetInfo(String jsonStr) {
        DiagramSheetParam sheetParam = checkParam(jsonStr);
        querySheetCIRltInfo(sheetParam);
        copyCIRltInfo(sheetParam);
        copyDiagramSheetInfo(sheetParam);
        return sheetParam.getCopySheetDataInfo();
    }

    /**
     *  校验传参
     * @param paramInfo
     */
    private DiagramSheetParam checkParam(String paramInfo) {
        JSONObject jsonObj = JSON.parseObject(paramInfo);
        String encryptDiagramId = jsonObj.getString("diagramId");
        if (BinaryUtils.isEmpty(encryptDiagramId)) {
            throw new BinaryException("参数缺失，视图id不能为空");
        }
        String sheetId = jsonObj.getString("sheetId");
        if (BinaryUtils.isEmpty(sheetId)) {
            throw new BinaryException("参数缺失，sheet页id不能为空");
        }
        DiagramSheetParam sheetParam = new DiagramSheetParam();
        List<ESDiagram> curDiagramList = esDiagramDao.getListByQuery(QueryBuilders.termQuery("dEnergy.keyword", encryptDiagramId));
        if (BinaryUtils.isEmpty(curDiagramList)) {
            throw new BinaryException("视图信息异常，请刷新页面");
        }
        sheetParam.setOwnerCode(curDiagramList.get(0).getOwnerCode());
        sheetParam.setId(curDiagramList.get(0).getId());
        sheetParam.setDiagramId(encryptDiagramId);
        sheetParam.setSheetId(sheetId);
        return sheetParam;
    }

    /**
     *  获取源sheet页内的node/link数据信息
     * @param sheetParam
     */
    private void querySheetCIRltInfo(DiagramSheetParam sheetParam) {
        // 获取当前sheet页私有库CI数据
        BoolQueryBuilder queryCIBool = QueryBuilders.boolQuery();
        queryCIBool.must(QueryBuilders.termQuery("dEnergy.keyword", sheetParam.getDiagramId()));
        queryCIBool.must(QueryBuilders.termQuery("sheetId.keyword", sheetParam.getSheetId()));
        List<ESDiagramNode> nodeList = esDiagramNodeDao.getListByQuery(queryCIBool);
        if (BinaryUtils.isEmpty(nodeList)) {
            log.info("#####视图sheet页生成副本，当前sheet页内不存在node");
            return;
        }
        sheetParam.setAllNodeList(nodeList);
        nodeList.stream().filter(each -> !BinaryUtils.isEmpty(each.getCiCode())).
                collect(Collectors.toMap(ESDiagramNode::getCiCode, each -> each, (k1, k2) -> k2));
        List<ESDiagramNode> ciNodeList = nodeList.stream().filter(node -> !BinaryUtils.isEmpty(node.getCiCode())).
                collect(Collectors.toList());
        if (BinaryUtils.isEmpty(ciNodeList)) {
            log.info("#####视图sheet页生成副本，当前sheet页内不存在CI数据");
            return;
        }
        sheetParam.setDataNodeList(ciNodeList);
        String ownerCode = sheetParam.getOwnerCode();
        List<String> nodeCodeList = ciNodeList.stream().map(ESDiagramNode::getCiCode).collect(Collectors.toList());
        CCcCi cdt = new CCcCi();
        cdt.setCiCodes(nodeCodeList.toArray(new String[0]));
        cdt.setOwnerCodeEqual(ownerCode);
        List<ESCIInfo> nodeInfoList = iamsESCIPrivateSvc.queryESCiInfoList(cdt, null, Boolean.FALSE);
        Set<Long> nodeClassIds = nodeInfoList.stream().map(ESCIInfo::getClassId).collect(Collectors.toSet());
        // 校验节点数据是否符合生成副本规则
        checkCIPKType(nodeClassIds);
        sheetParam.setHasCIData(Boolean.TRUE);
        sheetParam.setSourceCIList(nodeInfoList);
        // 获取当前sheet页私有库RLT数据
        BoolQueryBuilder queryRltBool = QueryBuilders.boolQuery();
        queryRltBool.must(QueryBuilders.termQuery("dEnergy.keyword", sheetParam.getDiagramId()));
        queryRltBool.must(QueryBuilders.termQuery("sheetId.keyword", sheetParam.getSheetId()));
        List<ESDiagramLink> rltLinkList = esDiagramLinkDao.getListByQuery(queryRltBool);
        if (BinaryUtils.isEmpty(rltLinkList)) {
            log.info("#####视图sheet页生成副本，当前sheet页内不存在link");
            return;
        }
        sheetParam.setAllLinkList(rltLinkList);
        List<ESDiagramLink> dataRltLinkList = rltLinkList.stream().filter(link -> !BinaryUtils.isEmpty(link.getUniqueCode())).
                collect(Collectors.toList());
        if (BinaryUtils.isEmpty(dataRltLinkList)) {
            log.info("#####视图sheet页生成副本，当前sheet页内不存在RLT数据");
            return;
        }
        sheetParam.setDataLinkList(dataRltLinkList);
        Set<String> rltCodeList = dataRltLinkList.stream().map(ESDiagramLink::getUniqueCode).collect(Collectors.toSet());
        ESRltSearchBean rltSearchBean = new ESRltSearchBean();
        rltSearchBean.setRltUniqueCodes(rltCodeList);
        rltSearchBean.setOwnerCode(ownerCode);
        rltSearchBean.setPageSize(rltCodeList.size());
        rltSearchBean.setPageNum(1);
        Page<ESCIRltInfo> rltInfoPage = iamsCIRltPrivateSvc.searchRlt(rltSearchBean);
        sheetParam.setSourceRltList(rltInfoPage.getData());
    }

    /**
     *  校验当前sheet页内的CI分类业务主键类型是否符 “合生成副本” 规则
     * @param classIds
     */
    private void checkCIPKType(Set<Long> classIds) {
        List<ESCIClassInfo> classList = esciClassSvc.getAllDefESClassInfosByClassIds(DOMAIN_ID, classIds);
        for (ESCIClassInfo classInfo : classList) {
            for (ESCIAttrDefInfo attrDef : classInfo.getAttrDefs()) {
                if (attrDef.getIsMajor() == 1) {
                    Integer proType = attrDef.getProType();
                    // 业务主键属性为 :1=整数 2=小数 3=短文本(<=200) 4=长文本(<=1000) 5=文章 6=枚举 7=日期 8=字典 9=3D模型 10=图片 11=关联属性 12=文档 150=编码类型
                    // todo 这里1和2类型的数据有规则 自增 比较复杂 后期有空再实现
                    List<Integer> restraintType = Arrays.asList(1, 2, 6, 7, 8, 9, 10, 11);
                    if (restraintType.contains(proType)) {
                        throw new BinaryException("sheet页内数据分类【" + classInfo.getClassCode() + "】定义的业务主键类型不适用生成副本规则，请修改分类业务主键类型");
                    }
                }
            }
        }
    }

    /**
     *  创建CI与RLT的副本数据
     * @param sheetParam
     */
    private void copyCIRltInfo(DiagramSheetParam sheetParam) {
        if (!sheetParam.getHasCIData()) {
            return;
        }
        // 创建CI/RLT副本
        List<EamCopyRltVO> rltParam = new ArrayList<>();
        if (!BinaryUtils.isEmpty(sheetParam.getSourceRltList())) {
            rltParam = EamUtil.copy(sheetParam.getSourceRltList(), EamCopyRltVO.class);
        }
        List<ESCIInfo> ciParam = sheetParam.getSourceCIList();

        EamCiRltCopyDTO copyParams = new EamCiRltCopyDTO();
        copyParams.setCiList(ciParam);
        copyParams.setRltList(rltParam);
        copyParams.setPostfix(CI_POSTFIX);
        copyParams.setOwnerCode(sheetParam.getOwnerCode());
        copyParams.setDiagramId(sheetParam.getDiagramId());
        EamCiRltCopyResult ciRltCopyResult = eamCiSvc.copyCiAndRltBatch(copyParams);

        // 组装返回CI/RLT 源端数据标识与副本数据组成map
        List<CiInfoExtend> copyCIList = EamUtil.copy(ciRltCopyResult.getCiList(), CiInfoExtend.class);
        Map<String, CiInfoExtend> ciMap = copyCIList.stream().collect(Collectors.toMap(CiInfoExtend::getOriginCiCode, e -> e, (k1, k2) -> k2));
        sheetParam.setSourceCiCodeAndCopyCIMap(ciMap);
        List<ESCIRltInfo> copyRltList = ciRltCopyResult.getRltList();
        Map<String, ESCIRltInfo> rltMap = copyRltList.stream().collect(Collectors.toMap(ESCIRltInfo::getOriginCode, e->e, (k1,k2)->k2));
        sheetParam.setSourceRltCodeAndCopyRltMap(rltMap);
    }

    /**
     *  创建视图sheet页副本
     * @param sheetParam
     */
    private void copyDiagramSheetInfo(DiagramSheetParam sheetParam) {
        // 获取视图内所有的sheet页 用于更新sheetOrder排序字段
        BoolQueryBuilder sheetBool = QueryBuilders.boolQuery();
        sheetBool.must(QueryBuilders.termQuery("diagramId", sheetParam.getId()));
        List<ESDiagramSheetDTO> diagramSheetInfoList = esDiagramSheetDao.getListByQuery(sheetBool);

        // 获取数据新旧map
        Map<String, CiInfoExtend> sourceCiCodeAndCopyCIMap = sheetParam.getSourceCiCodeAndCopyCIMap();
        Map<String, ESCIRltInfo> sourceRltCodeAndCopyRltMap = sheetParam.getSourceRltCodeAndCopyRltMap();

        List<ESDiagramSheetDTO> updateSheetList = new ArrayList<>();
        List<ESDiagramNode> copySheetNodeList = new ArrayList<>();
        List<ESDiagramLink> copySheetLinkList = new ArrayList<>();

        ExtendSheetInfo extendSheetInfo = new ExtendSheetInfo();
        // 基于源端sheet页创建副本sheet页
        for (ESDiagramSheetDTO sheetInfo : diagramSheetInfoList) {
            if (!sheetInfo.getSheetId().equals(sheetParam.getSheetId())) {
                continue;
            }
            ESDiagramSheetDTO copySheetInfo = EamUtil.copy(sheetInfo, ESDiagramSheetDTO.class);
            // 根据old-sheet页信息更新copy-sheet页
            copySheetInfo.setCopyId(sheetInfo.getSheetId());
            long uuid = ESUtil.getUUID();
            copySheetInfo.setId(uuid);
            String sheetUuid = UUID.randomUUID().toString();
            // 截取UUID的后8位作为随机字符串
            String randomString = sheetUuid.substring(sheetUuid.length() - 8);
            String copySheetId = "diagram-" + randomString;
            copySheetInfo.setSheetId(copySheetId);
            copySheetInfo.setSheetOrder(sheetInfo.getSheetOrder()+1);
            copySheetInfo.setName(sheetInfo.getName()+"-副本");
            esDiagramSheetDao.saveOrUpdate(copySheetInfo);
            extendSheetInfo = EamUtil.copy(copySheetInfo, ExtendSheetInfo.class);
            sheetParam.setCopySheetInfo(copySheetInfo);
            // 生成copy-node
            List<ESDiagramNode> allNodeList = sheetParam.getAllNodeList();
            for (ESDiagramNode node : allNodeList) {
                node.setId(null);
                node.setSheetId(copySheetId);
                if (!BinaryUtils.isEmpty(node.getCiCode())) {
                    CiInfoExtend ciInfoExtend = sourceCiCodeAndCopyCIMap.get(node.getCiCode());
                    refreshNode(node, ciInfoExtend);
                }
                copySheetNodeList.add(node);
            }
            extendSheetInfo.setNodeDataArray(copySheetNodeList);
            // 生成copy-link
            List<ESDiagramLink> allLinkList = sheetParam.getAllLinkList();
            for (ESDiagramLink link : allLinkList) {
                link.setId(null);
                link.setSheetId(copySheetId);
                if (!BinaryUtils.isEmpty(link.getUniqueCode())) {
                    ESCIRltInfo esciRltInfo = sourceRltCodeAndCopyRltMap.get(link.getUniqueCode());
                    refreshLink(link, esciRltInfo);
                }
                copySheetLinkList.add(link);
            }
            extendSheetInfo.setLinkDataArray(copySheetLinkList);
        }
        for (ESDiagramSheetDTO sheetInfo : diagramSheetInfoList) {
            if (BinaryUtils.isEmpty(sheetInfo.getSheetOrder())) {
                log.info("#####视图sheet页生成副本，当前sheet页内序号异常,diagramId:{},sheetId:{},exceptionSheetInfo:{}",
                        sheetParam.getDiagramId(), sheetParam.getSheetId(), JSONObject.toJSONString(sheetInfo));
                throw new ServerException("sheet页排序异常");
            }
            if (sheetInfo.getSheetOrder() >= sheetParam.getCopySheetInfo().getSheetOrder()) {
                sheetInfo.setSheetOrder(sheetInfo.getSheetOrder()+1);
                // 更新源端sheet页之后的信息即可
                updateSheetList.add(sheetInfo);
            }
        }
        esDiagramSheetDao.saveOrUpdateBatch(updateSheetList);
        esDiagramNodeDao.saveOrUpdateBatch(copySheetNodeList);
        esDiagramLinkDao.saveOrUpdateBatch(copySheetLinkList);
        sheetParam.setCopySheetDataInfo(extendSheetInfo);
    }

    /**
     *  刷新node节点内的数据标识信息
     * @param node
     * @param ciInfoExtend
     */
    private void refreshNode(ESDiagramNode node, CiInfoExtend ciInfoExtend) {
        JSONObject jsonObject = JSON.parseObject(node.getNodeJson());
        if (BinaryUtils.isEmpty(ciInfoExtend)) {
            log.error("#####视图sheet页生成副本，刷新node节点异常，源节点ciCode：{}", node.getCiCode());
            // 清空ciCode/ciId
            node.setCiCode("");
            jsonObject.put("ciId", "");
            jsonObject.put("ciCode", "");
        } else {
            node.setCiCode(ciInfoExtend.getCi().getCiCode());
            jsonObject.put("ciId", ciInfoExtend.getCi().getId());
            jsonObject.put("ciCode", ciInfoExtend.getCi().getCiCode());
            jsonObject.put("ciPrimaryKey", ciInfoExtend.getCi().getCiPrimaryKey());
            String ciLabelStr = ciInfoExtend.getCi().getCiLabel();
            // 更新label 分类定义勾选label为主 没有的话使用分类定义className
            String oldLabel = jsonObject.getString("label");
            if (oldLabel.equals(ciInfoExtend.getCiClass().getClassName())) {
                // 暂时按照前端逻辑维持原值不变
            } else {
                List<String> ciLabelList = JSONObject.parseArray(ciLabelStr, String.class);
                String nodeCiLabel = String.join("\n", ciLabelList);
                jsonObject.put("label", nodeCiLabel);
            }
        }
        node.setNodeJson(jsonObject.toJSONString());
    }

    /**
     *  刷新link节点内的数据标识信息
     * @param link
     * @param esciRltInfo
     */
    private void refreshLink(ESDiagramLink link, ESCIRltInfo esciRltInfo) {
        if (BinaryUtils.isEmpty(esciRltInfo)) {
            log.error("#####视图sheet页生成副本，刷新link节点异常，源节点uniqueCode：{}", link.getUniqueCode());
            return;
        }
        link.setUniqueCode(esciRltInfo.getUniqueCode());
        JSONObject jsonObject = JSON.parseObject(link.getLinkJson());
        jsonObject.put("rltId", esciRltInfo.getId());
        jsonObject.put("rltCode", esciRltInfo.getUniqueCode());
        link.setLinkJson(jsonObject.toJSONString());
    }

}
